<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <p v-text="name"></p>
    <input type="text" v-model="name">
<!--    <p v-text="age"></p>
    <input type="text" v-model="age">-->
</div>

<script>
    // 引入发布订阅模式
    const Dep = {
        map: {},
        // 收集事件的方法
        collect(eventName, fn) {
            console.log("collect")
            console.log("eventName",eventName)
            console.log("fn",fn)
            // 如果当前map中已经初始化好了 click:[]
            // 就直接往里面push  如果没有初始化首次添加  就先进行初始化
            if (!this.map[eventName]) {
                this.map[eventName] = []
            }
            this.map[eventName].push(fn)
        },
        // 触发事件的方法
        trigger(eventName) {
            this.map[eventName].forEach(fn => fn())
        }
    }

    let data = {
        name: '柴达木',
        age: 17
    }
    // 把data中的属性变成响应式的
    Object.keys(data).forEach((key) => {
        defineReactive(data, key, data[key])
    })
    function defineReactive(data, key, value) {
        console.log("defineReactive")
        // 进行转换操作
        Object.defineProperty(data, key, {
            get() {
                return value
            },
            set(newValue) {
                console.log("defineProperty的set方法执行")
                // 如果新旧值相等，不执行后续逻辑
                if (newValue === value) {
                    return
                }
                // 把最新的值 反映到视图中  这里是关键的位置
                value = newValue
                // 核心：操作dom  就是通过操作dom api 把最新的值设置上去
                // 在这里进行精准更新 -> 通过data中的属性名找到对应的更新函数依次执行
                Dep.trigger(key)
            }
        })
    }
    // 1.通过标识查找把数据放到对应的dom上显示出来
    function compile() {
        let app = document.getElementById('app')
        // 拿到所有节点
        const childNodes = app.childNodes // 所有类型的节点包括文本节点和标签节点
        childNodes.forEach(node => {
            if (node.nodeType === 1) {
                const attrs = node.attributes
                Array.from(attrs).forEach(attr => {
                    const nodeName = attr.nodeName
                    const nodeValue = attr.nodeValue
                    // 实现v-text
                    if (nodeName === 'v-text') {
                        node.innerText = data[nodeValue]
                        // 收集更新函数
                        Dep.collect(nodeValue, () => {
                            console.log(`当前您修改了属性${nodeValue}`)
                            node.innerText = data[nodeValue]
                        })
                    }
                    // 实现v-model
                    if (nodeName === 'v-model') {
                        // 调用dom操作给input标签绑定数据
                        node.value = data[nodeValue]
                        // 收集更新函数
                        Dep.collect(nodeValue,()=>{
                            node.value = data[nodeValue]
                        })
                        // 监听input事件 在事件回调中 拿到最新的输入值 赋值给绑定的属性
                        node.addEventListener('input', (e) => {
                            // 反向赋值
                            console.log("input监听器执行，此处会导致data内数据变动，触发defineProperty的set方法")
                            data[nodeValue] = e.target.value
                        })
                    }
                })
            }
        })
    }
    compile()
    console.log(Dep)
</script>
</body>
</html>
